package com.cavy.platform.client.domain;

import com.cavy.platform.client.BeansProvider;
import com.cavy.platform.client.message.Message;
import com.cavy.platform.client.message.StageExecutedErrorMessage;
import com.cavy.platform.client.message.StageExecutedSuccessMessage;
import com.cavy.platform.client.service.WebSocketService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author 唐旭
 * @Date 2018/7/18
 */
@Data
@Slf4j
public class Stage implements ContainerRunnable {
    private String name;
    private List<ContainerInfo> containerList = new ArrayList<>();
    private CountDownLatch latch;
    private List<FutureTask<Boolean>> tasks;
    private boolean error = false;

    @Override
    public void run(RunningParameter parameter, int index) throws ContainerRunningException {
        latch = new CountDownLatch(containerList.size());
        tasks = new ArrayList<>(containerList.size());
        //并行运行
        for (int i = 0; i < containerList.size(); i++) {
            ContainerInfo containerInfo = containerList.get(i);
            startThread(containerInfo, parameter, i);
        }

        Message message = null;
        try {
            //等待并计算结果
            message = getMessage(parameter);
        } catch (Exception e) {
            message = setException(e, parameter);
            throw e;
        } finally {
            //发送消息
            sendMessage(message);
        }
    }

    @Override
    public void stop() throws InterruptedException, ContainerRunningException {
        for (int i = 0; i < tasks.size(); i++) {
            FutureTask<Boolean> task = tasks.get(i);
            //停止所有task
            if (!task.isDone()) {
                ContainerInfo containerInfo = containerList.get(i);
                containerInfo.stop(false, i);
            }

        }
    }

    /**
     * 计算结果
     * 1,不能超时(await=true);2,不能抛出异常(get不出现Exception);
     *
     * @param parameter 运行参数
     * @return 计算结果消息
     * @throws ContainerRunningException 运行出现的异常
     */
    private Message getMessage(RunningParameter parameter) throws ContainerRunningException {
        try {
            boolean await = latch.await(getTimeOut(parameter), TimeUnit.MILLISECONDS);
            if (await) {
                //发送任务结束消息
                for (FutureTask<Boolean> task : tasks) {
                    task.get();
                }
            } else {
                stop();
            }
            return buildMessage(parameter);
        } catch (ExecutionException e) {
            Thread.currentThread().interrupt();
            Throwable cause = e.getCause();
            throw new ContainerRunningException(cause == null ? e.getMessage() : cause.getMessage());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new ContainerRunningException("操作被中断");
        }
    }

    private Message buildMessage(RunningParameter parameter) {
        for (ContainerInfo containerInfo : containerList) {
            if (!containerInfo.isSuccess()) {
                error = true;
            }
        }
        if (error) {
            return new StageExecutedErrorMessage(parameter, "容器退出状态码不为0");
        }
        return new StageExecutedSuccessMessage(parameter);
    }

    private Message setException(Exception e, RunningParameter parameter) {
        return new StageExecutedErrorMessage(parameter, e.getMessage());
    }

    private void sendMessage(Message message) {
        WebSocketService bean = BeansProvider.getBean(WebSocketService.class);
        assert bean != null;
        bean.send(message);
    }

    private long getTimeOut(RunningParameter parameter) {
        Date runningTimeOut = parameter.getRunningTimeOut();
        return runningTimeOut.getTime() - new Date().getTime();
    }

    private void startThread(ContainerInfo containerInfo, RunningParameter parameter, int index) {
        parameter.setStageName(name);
        //修改为callable
        FutureTask<Boolean> task = new FutureTask<>(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                try {
                    containerInfo.run(parameter, index);
                } finally {
                    latch.countDown();
                }
                return true;
            }
        });
        Thread thread = new Thread(task);
        thread.setName(parameter.getThreadName() + RunningParameter.THREAD_NAME_SEPARATOR + name);
        thread.start();
        tasks.add(task);
    }

    public boolean hasError() {
        return error;
    }
}
